namespace ShellNavigator.Client.Behaviour
{
  #region

  using System;
  using System.Windows;
  using System.Windows.Input;

  #endregion

  public static class EventBehaviourFactory
  {
    #region Public Methods

    public static DependencyProperty CreateCommandExecutionEventBehaviour(
      RoutedEvent routedEvent, string propertyName, Type ownerType)
    {
      DependencyProperty property = DependencyProperty.RegisterAttached(
        propertyName, 
        typeof(ICommand), 
        ownerType, 
        new PropertyMetadata(null, new ExecuteCommandOnRoutedEventBehaviour(routedEvent).PropertyChangedHandler));

      return property;
    }

    #endregion

    internal abstract class ExecuteCommandBehaviour
    {
      #region Constants and Fields

      protected DependencyProperty property;

      #endregion

      #region Public Methods

      public void PropertyChangedHandler(DependencyObject sender, DependencyPropertyChangedEventArgs e)
      {
        // the first time the property changes,
        // make a note of which property we are supposed
        // to be watching
        if (this.property == null)
        {
          this.property = e.Property;
        }

        object oldValue = e.OldValue;
        object newValue = e.NewValue;

        this.AdjustEventHandlers(sender, oldValue, newValue);
      }

      #endregion

      #region Methods

      protected abstract void AdjustEventHandlers(DependencyObject sender, object oldValue, object newValue);

      protected void HandleEvent(object sender, EventArgs e)
      {
        DependencyObject dp = sender as DependencyObject;
        if (dp == null)
        {
          return;
        }

        ICommand command = dp.GetValue(this.property) as ICommand;

        if (command == null)
        {
          return;
        }

        if (command.CanExecute(e))
        {
          command.Execute(e);
        }
      }

      #endregion
    }

    /// <summary>
    ///     An internal class to handle listening for an event and executing a command,
    ///     when a Command is assigned to a particular DependencyProperty
    /// </summary>
    private class ExecuteCommandOnRoutedEventBehaviour : ExecuteCommandBehaviour
    {
      #region Constants and Fields

      private readonly RoutedEvent routedEvent;

      #endregion

      #region Constructors and Destructors

      public ExecuteCommandOnRoutedEventBehaviour(RoutedEvent routedEvent)
      {
        this.routedEvent = routedEvent;
      }

      #endregion

      #region Methods

      protected override void AdjustEventHandlers(DependencyObject sender, object oldValue, object newValue)
      {
        UIElement element = sender as UIElement;
        if (element == null)
        {
          return;
        }

        if (oldValue != null)
        {
          element.RemoveHandler(this.routedEvent, new RoutedEventHandler(this.EventHandler));
        }

        if (newValue != null)
        {
          element.AddHandler(this.routedEvent, new RoutedEventHandler(this.EventHandler));
        }
      }

      protected void EventHandler(object sender, RoutedEventArgs e)
      {
        this.HandleEvent(sender, e);
      }

      #endregion
    }
  }
}